from typing import Dict
from typing import List
import re


class Convertor:

    def convert(self, code) -> str:
        return code


class PinyinConvertor(Convertor):

    def __init__(self, sheng: Dict[str, str], yun: Dict[str, str], special: Dict[str, str]) -> None:
        super().__init__()
        self.sheng = sheng
        self.yun = yun
        self.special = special

    def convert(self, code) -> str:
        return self._pinyin_to_double(code)

    def _pinyin_to_double(self, pinyin: str) -> str:
        rtn = []
        code = pinyin.strip()
        if code.find(' ') > 0:  # 词组
            for c in code.split(' '):
                rtn.append(self._get_double(c))
        else:
            rtn.append(self._get_double(code))
        return rtn[0] if len(rtn) == 1 else ' '.join(rtn)

    def _get_double(self, pinyin: str) -> str:
        if pinyin in self.special:
            return self.special.get(pinyin)
        if len(pinyin) == 1:
            return pinyin * 2
        first = pinyin[0]
        remains = pinyin[1:]
        if remains.startswith('h'):  # zh ch sh
            code = self.sheng.get(first + 'h')
            code += self.yun.get(remains[1:])
        else:
            code = self.sheng.get(first)
            code += self.yun.get(remains)
        return code


class RegCodeConvertor(Convertor):
    def __init__(self, rules: List[str]) -> None:
        super().__init__()
        self.rules = rules or []

    def convert(self, code) -> str:
        new_code = code
        if code.find(' ') > 0:  # 词组
            codes = []
            for c in code.split(' '):
                new_code = c
                for rule in self.rules:
                    new_code = self._replace(new_code, rule)
                codes.append(new_code)
            new_code = ' '.join(codes)
        else:  # 单字
            for rule in self.rules:
                new_code = self._replace(new_code, rule)
        return new_code

    def _replace(self, code, rule):
        express = rule.split('/')
        if re.search(express[1], code):
            return re.sub(express[1], express[2], code)
        return code
